<html>
<head>
<title>Blobs</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<script id="screen_clamp_shader-vs" type="x-shader/x-vertex">
    attribute vec2 aVertexPosition;
    varying vec2 vTextureCoord;
    void main(void) {
        gl_Position = vec4(aVertexPosition,0.0, 1.0);
		vTextureCoord = aVertexPosition;
    }
</script>

<script id="screen_clamp_shader-fs" type="x-shader/x-fragment">
    precision mediump float;
    varying vec2 vTextureCoord;
    uniform mat4 mView;
	uniform vec2 mTime;
	
	#define EPSILON 0.001
	#define VIEW_LEN 40.0

	float DistanceToSphere( vec3 center, float radius, vec3 pos )
	{
		return length ( center - pos ) - radius;
	}
	float DistanceToPlane( vec4 plane, vec3 pos )
	{
		return dot(plane.xyz, pos ) - plane.w;
	}
	float DistanceToAABB( vec3 center, vec3 extents, vec3 pos )
	{
		return length(max(abs(pos-center)-extents,0.0));
	}

		

	vec3 Bend(vec3 pos, vec3 center, float scale)
	{
		pos = pos-center;
		float c = cos(scale*pos.y);
		float s = sin(scale*pos.y);
		mat2  m = mat2(c,s,-s,c);
		return vec3(m*pos.xz,pos.y)+center;
	}
	vec3 Repeat( vec3 pos, vec3 size )
	{
		return mod(pos,size)-0.5*size;
	}
	float Blend( float d1, float d2, float scale)
	{
		float dd = smoothstep(0.0,scale,d1-d2);
		return mix(d1,d2,dd);
	}
	float displacement(vec3 pos)
	{
		pos = pos *0.003;
		float distance = (sin(967.0*pos.x+mTime.x*2.0)+sin(971.0*pos.y)+sin(977.0*pos.z));
		return  distance;
	}
	
	float Ship(in vec3 pos)
	{	
		float part0Distance = DistanceToSphere(vec3(1.0,0.0,0.0), 3.0, pos*vec3(0.8,1.0,1.0));
		float part1Distance = DistanceToSphere(vec3(-2.0,0.0,-1.3), 2.0, pos);
		float part2Distance = DistanceToAABB(vec3(3.0,0.0,1.0), vec3(1.0,3.0,0.2), pos)-0.3;
		//return part2Distance;
		return Blend(Blend(part2Distance, part0Distance ,3.0),part1Distance, 1.0)-max(displacement(pos),0.0)*0.1;
		//float distance = Blend(Blend(part0Distance, part1Distance),part2Distance);
		
		//return distance;
	}
	
	float DistanceAtPoint( in vec3 pos)
	{
		//pos = Repeat(pos,vec3(3.0,3.0,3.0));
		return Ship(pos);
	}
	
	vec3 NormalAtPoint( vec3 pos, float distance )
	{
		vec3 n = vec3(0.0,0.0,0.0);
		vec3 offset = vec3 ( 0.1, 0.0, 0.0 );
		
		n.x = DistanceAtPoint ( pos + offset.xyz ) - distance;
		n.y = DistanceAtPoint ( pos + offset.yxz ) - distance;
		n.z = DistanceAtPoint ( pos + offset.yzx ) - distance;
		
		return normalize ( n );
	}
	/*
	float softshadow( in vec3 ro, in vec3 rd, float mint, float maxt, float k )
	{
		float res = 1.0;
		for( float t=mint; t < maxt; )
		{
			float h = map(ro + rd*t);
			if( h<0.001 )
				return 0.0;
			res = min( res, k*h/t );
			t += h;
		}
		return res;
	}
	*/
    void main(void) {
	
		float ScreenRatio = 3.0/4.0;
		float FOVlength = 2.0;
	
		vec3 rayDir =  mView[0].xyz * vTextureCoord.x;
		rayDir += mView[1].xyz * vTextureCoord.y * ScreenRatio;
		rayDir += mView[2].xyz * FOVlength;
		
		vec3  rayPosition = mView[3].xyz;
		rayDir = normalize ( rayDir );
		vec3  color = vec3(0.0,0.0,0.0);
		vec3 lightPos = vec3(10.0,10.0,10.0);
		
		float totalDistance = 0.0;
		for (int i=0; i < 128; i++)
		{
			if (totalDistance > VIEW_LEN)
				break;
			float distance = DistanceAtPoint(rayPosition);
			totalDistance += distance;
			if (distance < EPSILON)
			{
			//	color = vec3(totalDistance/10.0,0.0,0.0);
				vec3 lightDir = normalize(lightPos-rayPosition);
				vec3 normal = NormalAtPoint(rayPosition,distance);
				
				float distanceInNormalDir = max(
				DistanceAtPoint(rayPosition+normal*0.2)*5.0*0.25+
				DistanceAtPoint(rayPosition+normal*0.5)*2.0*0.25+
				DistanceAtPoint(rayPosition+normal*1.0)*1.0*0.25+
				DistanceAtPoint(rayPosition+normal*2.0)*0.5*0.25
				,0.0)*0.8;
				
				color = max(dot(lightDir,normal),0.1)*vec3(max(sign(displacement(rayPosition)),0.0),2.0,1.0)*distanceInNormalDir*distanceInNormalDir;
				break;
			}
			rayPosition += distance*rayDir*0.7;
		}
		gl_FragColor = vec4(color,1.0);
    }
</script>


<script type="text/javascript">

    var gl;
    var screenClampShaderProgram;
    var squareVertexPositionBuffer;
	var startTime;
	var canvas;
	var mousePos = [0.0,0.0];
    function initGL(canvas) {
        try {
            gl = canvas.getContext("experimental-webgl");
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }

    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }

	function initScreenClampShader() {
        var fragmentShader = getShader(gl, "screen_clamp_shader-fs");
        var vertexShader = getShader(gl, "screen_clamp_shader-vs");

        screenClampShaderProgram = gl.createProgram();
        gl.attachShader(screenClampShaderProgram, vertexShader);
        gl.attachShader(screenClampShaderProgram, fragmentShader);
        gl.linkProgram(screenClampShaderProgram);

        if (!gl.getProgramParameter(screenClampShaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }

        gl.useProgram(screenClampShaderProgram);

        screenClampShaderProgram.vertexPositionAttribute = gl.getAttribLocation(screenClampShaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(screenClampShaderProgram.vertexPositionAttribute);

        screenClampShaderProgram.mView = gl.getUniformLocation(screenClampShaderProgram, "mView");
		screenClampShaderProgram.Time = gl.getUniformLocation(screenClampShaderProgram, "mTime");
	}

    function initSquareBuffer() {
        squareVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
        vertices = [
             1.0,  1.0,
            -1.0,  1.0,
             1.0, -1.0,
            -1.0, -1.0
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        squareVertexPositionBuffer.itemSize = 2;
        squareVertexPositionBuffer.numItems = 4;
    }
	
	function updateScene() {
	}
	function dot(vec0,vec1)
	{
		return vec0[0]*vec1[0]+vec0[1]*vec1[1]+vec0[2]*vec1[2];
	}
	function sub(vec0,vec1)
	{
		return [vec0[0]-vec1[0],vec0[1]-vec1[1],vec0[2]-vec1[2]];
	}
	function normalize(vec)
	{
		var len = Math.sqrt(dot(vec,vec));
		return [vec[0]/len, vec[1]/len, vec[2]/len];
	}
	function cross(vec0,vec1)
	{
		var x = vec0[1]*vec1[2] - vec0[2]*vec1[1]; 
		var y = vec0[2]*vec1[0] - vec0[0]*vec1[2]; 
		var z = vec0[0]*vec1[1] - vec0[1]*vec1[0];
		return [x,y,z];
	}
	function lookAt(eye, at, up)
	{
		var zaxis = normalize(sub(at, eye));
		var xaxis = normalize(cross(up, zaxis));
		var yaxis = cross(zaxis, xaxis);	
		/*return [
		xaxis[0], yaxis[0], zaxis[0], 0,
		xaxis[1], yaxis[1], zaxis[1], 0,
		xaxis[2], yaxis[2], zaxis[2], 0,
		-dot(xaxis, eye), -dot(yaxis, eye), -dot(zaxis, eye), 1
		];*/
		// inverse
		return [
		xaxis[0], xaxis[1],xaxis[2], 0,
		yaxis[0], yaxis[1], yaxis[2], 0,
		zaxis[0], zaxis[1], zaxis[2], 0,
		eye[0], eye[1], eye[2], 1
		];
		
	}
	function drawScene() {
		
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
		gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
    	
		gl.clearColor(0.0, 0.0, 0.0, 0.0);
		gl.clearDepth(1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
		
	
		viewMat = lookAt([Math.cos(mousePos[0]*0.5)*20,Math.sin(mousePos[0]*0.5)*20,mousePos[1]], [0,0,0],[0,0,1]);
		
        gl.useProgram(screenClampShaderProgram);
	    gl.uniformMatrix4fv(screenClampShaderProgram.mView, false,viewMat);
		var d=new Date();
		var t=(d.getTime()-startTime.getTime())/1000;
		gl.uniform2fv(screenClampShaderProgram.Time, [t,0]);
		gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
		gl.vertexAttribPointer(screenClampShaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
		
		gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
    	
    }
	

    function tick() {
        requestAnimFrame(tick);
		updateScene();
        drawScene();
    }
	

    function handleMouseMove(event) {
		mousePos[0] = -((event.clientX/canvas.width)*2.0-1.0)*10;
		mousePos[1] = ((event.clientY/canvas.height)*2.0-1.0)*10;
	}
    function webGLStart() {
        canvas = document.getElementById("blob-canvas");
		
        document.onmousemove = handleMouseMove;
		
        initGL(canvas);
        initScreenClampShader();
        initSquareBuffer();
		startTime = new Date();
		tick();       
    }
	
	window.requestAnimFrame = (function() {
	  return window.requestAnimationFrame ||
			 window.webkitRequestAnimationFrame ||
			 window.mozRequestAnimationFrame ||
			 window.oRequestAnimationFrame ||
			 window.msRequestAnimationFrame ||
			 function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
			   window.setTimeout(callback, 1000/60);
			 };
	})();


</script>
</head>
<body onload="webGLStart();">
    <canvas id="blob-canvas" style="border: none;" width="640" height="480"></canvas>
</body>
</html>
